import net from 'net';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { fork } from 'child_process';
import { fileURLToPath } from 'url';

const PORT = 5000
const SAMPLE_RATE = 16000;
const CHANNELS = 1;
const BIT_DEPTH = 16;
const OUTPUT_DIR = './temp_audio';
const SEGMENT_BYTES = 1024 * 512;

if (!fs.existsSync(OUTPUT_DIR)) {
  fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}

// 子进程：专门写 WAV，避免主进程阻塞
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const workerPath = path.join(__dirname, 'wav_worker.js');
const worker = fork(workerPath, [], { stdio: ['ignore', 'inherit', 'inherit', 'ipc'] });
let workerReady = false;
let workerExited = false;
let isShuttingDown = false;

worker.on('message', (msg) => {
  if (!msg || typeof msg !== 'object') return;
  if (msg.type === 'ready') workerReady = true;
  if (msg.type === 'error') console.error('写入子进程错误:', msg.message);
  if (msg.type === 'exited') workerExited = true;
});

worker.on('exit', (code, signal) => {
  workerExited = true;
  if (code !== 0) console.error('写入子进程异常退出:', code, signal || '');
});

worker.on('error', (err) => {
  workerExited = true;
  console.error('写入子进程进程错误:', err?.message || err);
});

worker.send({
  type: 'init',
  config: {
    sampleRate: SAMPLE_RATE,
    channels: CHANNELS,
    bitDepth: BIT_DEPTH,
    outputDir: OUTPUT_DIR,
  },
});

const sockets = new Set();
let fileSeq = 0;

const server = net.createServer((socket) => {
  console.log('TCP 客户端已连接:', `${socket.remoteAddress}:${socket.remotePort}`);
  sockets.add(socket);

  const pendingBuffers = [];
  let pendingBytes = 0;
  let total = 0;

  // 将精确字节数从缓存中剥离出来，发送给子进程
  function sendToWorker(pcm) {
    const name = `tcp_audio_${Date.now()}_${++fileSeq}.wav`;
    const localSave = () => {
      try {
        const dataLength = pcm.length;
        const fileLength = 36 + dataLength;
        const header = Buffer.alloc(44);
        header.write('RIFF', 0);
        header.writeUInt32LE(fileLength, 4);
        header.write('WAVE', 8);
        header.write('fmt ', 12);
        header.writeUInt32LE(16, 16);
        header.writeUInt16LE(1, 20);
        header.writeUInt16LE(CHANNELS, 22);
        header.writeUInt32LE(SAMPLE_RATE, 24);
        header.writeUInt32LE(SAMPLE_RATE * CHANNELS * (BIT_DEPTH / 8), 28);
        header.writeUInt16LE(CHANNELS * (BIT_DEPTH / 8), 32);
        header.writeUInt16LE(BIT_DEPTH, 34);
        header.write('data', 36);
        header.writeUInt32LE(dataLength, 40);
        const wav = Buffer.concat([header, pcm]);
        const fallback = path.join(OUTPUT_DIR, `fallback_${name}`);
        fs.writeFileSync(fallback, wav);
        console.log('子进程不可用，主进程回退保存:', fallback, 'PCM字节:', pcm.length);
      } catch (e) {
        console.error('主进程回退保存失败:', e?.message || e);
      }
    };

    if (isShuttingDown || workerExited || !worker?.connected) {
      console.warn('子进程未连接或已退出，回退保存');
      return localSave();
    }
    console.log('触发子进程保存:', name, 'PCM字节:', pcm.length);
    try {
      worker.send({ type: 'segment', pcm, name });
    } catch (e) {
      console.error('发送到子进程失败:', e?.message || e);
      localSave();
    }
  }

  function writeSegment(bytesToWrite) {
    let remaining = bytesToWrite;
    const parts = [];
    while (remaining > 0 && pendingBuffers.length > 0) {
      const first = pendingBuffers[0];
      if (first.length <= remaining) {
        parts.push(first);
        pendingBuffers.shift();
        pendingBytes -= first.length;
        remaining -= first.length;
      } else {
        const slice = first.subarray(0, remaining);
        const rest = first.subarray(remaining);
        parts.push(slice);
        pendingBuffers[0] = rest;
        pendingBytes -= remaining;
        remaining = 0;
      }
    }
    if (parts.length === 0) return;
    const pcm = Buffer.concat(parts);
    sendToWorker(pcm);
  }

  // 提供给优雅停止时调用的上下文方法
  socket._ctx = {
    getPendingBytes: () => pendingBytes,
    writeAllPending: () => {
      if (pendingBytes > 0) writeSegment(pendingBytes);
    },
  };

  let index = 0;

  socket.on('data', (buf) => {
    index++;
    if (index % 100 === 0) {
        console.log('receive data', buf.length)
    }
    
    pendingBuffers.push(buf);
    pendingBytes += buf.length;
    const prevTotal = total;
    total += buf.length;
    if (Math.floor(total / 102400) > Math.floor(prevTotal / 102400)) {
      console.log('已接收字节:', total);
    }
    if (pendingBytes >= SEGMENT_BYTES) {
      console.log('达到分片阈值，准备写出，pendingBytes=', pendingBytes);
    }
    while (pendingBytes >= SEGMENT_BYTES) {
      writeSegment(SEGMENT_BYTES);
    }
  });

  socket.on('end', () => {
    console.log('TCP 客户端已断开, 总字节:', total);
    if (pendingBytes > 0) {
      writeSegment(pendingBytes);
    }
  });

  socket.on('close', () => {
    sockets.delete(socket);
  });

  socket.on('error', (err) => {
    console.error('TCP 连接错误:', err?.message || err);
  });
});

server.listen(PORT, () => {
  console.log(`TCP 服务器已启动，监听端口 ${PORT}`);
  console.log('分片阈值(字节)=', SEGMENT_BYTES);
  const nets = os.networkInterfaces();
  const addrs = [];
  for (const name of Object.keys(nets)) {
    for (const n of nets[name] || []) {
      if (n && n.family === 'IPv4' && !n.internal) addrs.push(n.address);
    }
  }
  if (addrs.length === 0) {
    console.log(`可用地址: 127.0.0.1:${PORT}`);
  } else {
    console.log('可用地址:');
    for (const a of addrs) console.log(`  ${a}:${PORT}`);
  }
});

process.on('SIGINT', () => {
  console.log('\n正在停止 TCP 服务器...');
  isShuttingDown = true;
  // 先停止接受新连接
  try { server.close(); } catch {}

  // 先把每个连接的剩余缓存写出
  for (const s of sockets) {
    const ctx = s && s._ctx;
    if (ctx && ctx.getPendingBytes && ctx.writeAllPending) {
      try { ctx.writeAllPending(); } catch {}
    }
  }

  // 然后销毁连接，避免继续读取
  for (const s of sockets) {
    try { s.destroy(); } catch {}
  }

  // 通知子进程退出（在它写完队列后会发 exited）
  if (!workerExited && worker?.connected) {
    try { worker.send({ type: 'exit' }); } catch {}
  }

  // 等待子进程退出或兜底超时
  const timeout = setTimeout(() => process.exit(0), 5000);
  timeout.unref();
  const check = setInterval(() => {
    if (workerExited) {
      clearInterval(check);
      clearTimeout(timeout);
      process.exit(0);
    }
  }, 100);
  check.unref();
});


